<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>系统图谱展示</title>
  <script src="./assets/axios.min.js"></script>
  <script src="./assets/echarts.js"></script>
  <link rel="stylesheet" type="text/css" href="assets/materialdesignicons.min.css">

  <script type="text/javascript">
    //创建axios实例
    const axiosInstance = axios.create({
      baseURL: 'http://39.97.209.211:5000/',
      timeout: 6000,
    })
    //axios配置拦截器
    axios.interceptors.response.use((response) => {
      return response;
    }, (error) => {
      alert("An error occurred with network");
      console.log(error);
      return Promise.reject(error);
    })
    //对使用axios访问各api的封装
    var api = {
      getAll: () => {
        return axiosInstance.get('all');
      },
      downloadWsdl: (id) => {
        return axiosInstance.get(`download?id=${id}`);
      },
      deleteNode: (id) => {
        return axiosInstance.get(`delete?id=${id}`);
      },
      createNode: (nodeInfo) => {
        return axiosInstance.post(`add`, nodeInfo);
      },
      searchNode: (keyword) => {
        return axiosInstance.get(`search?keyword=${keyword}`);
      }
    }    
  </script>
</head>
<body>
  <!--登录遮罩-->
  <div id="login" class="enter">
    <canvas id="effect" style="width: 100%; height: 100%;"></canvas>
    <div class="content-wrapper">
      <h1>WSDL系统图谱交互平台</h1>
      <div class="card">
        <div class="item">
          <span class="label">账 号</span>
          <input id="login-account-input">
        </div>
        <div class="item">
          <span class="label">密 码</span>
          <input type="password" id="login-password-input">
        </div>
        <div class="item">
          <div class="button" onclick="login()">登 录</div>
        </div>
      </div>
    </div>
  </div>
  <!--背景效果控制代码-->
  <script type="text/javascript">
    let effect = document.getElementById('effect');
    var w = effect.width = window.innerWidth,
    h = effect.height = window.innerHeight,
    ctx = effect.getContext('2d'),
    
    minDist = 5, maxDist = 15, initialWidth = 2, maxLines = 30, initialLines = 4, speed = 3,
    lines = [],
    frame = 0,
    timeSinceLast = 0,
    
    dirs = [[0, 1], [1, 0], [0, -1], [-1, 0], [0, 1], [1, 0], [0, -1], [-1, 0], [0, 1], [1, 0], [0, -1], [-1, 0], [.7, .7], [.7, -.7], [-.7, .7], [-.7, -.7]],
    starter = { x:w/2, y:h/2, vx:0, vy:0, width: initialWidth };

    function init() {
      lines.length = 0;
      for( var i = 0; i < initialLines; ++i) lines.push(new Line(starter));
      ctx.fillStyle = 'rgb(15, 23, 49)';
      ctx.fillRect(0,0,w,h);
      ctx.lineCap = 'round';
    }
    function anim() {
      window.requestAnimationFrame( anim );
      ++frame;
      ctx.shadowBlur = 0;
      ctx.fillStyle = '#191b4502';
      ctx.fillRect( 0, 0, w, h );
      ctx.shadowBlur = .5;
      for(var i=0;i<lines.length; ++i) if(lines[i].step()) { lines.splice(i,1); --i; }
      ++timeSinceLast
      if( lines.length < maxLines && timeSinceLast > 10 && Math.random() < .5 ) {
        timeSinceLast = 0;
        lines.push(new Line(starter));
        ctx.fillStyle = ctx.shadowColor = '#2c2';
        ctx.beginPath();
        ctx.arc(starter.x, starter.y, initialWidth, 0, Math.PI * 2);
        ctx.fill();
      }
    }

    function Line(parent) {
      this.x = parent.x | 0;
      this.y = parent.y | 0;
      this.width = parent.width / 1.25;
      do {
        var dir = dirs[(Math.random() * dirs.length) | 0];
        this.vx = dir[0];
        this.vy = dir[1];
      } while ((this.vx === -parent.vx && this.vy === -parent.vy) || (this.vx === parent.vx && this.vy === parent.vy));
      this.vx *= speed;
      this.vy *= speed;
      this.dist = (Math.random() * (maxDist - minDist) + minDist);
    }
    Line.prototype.step = function() {
      var dead = false;
      var prevX = this.x, prevY = this.y;
      this.x += this.vx;
      this.y += this.vy;
      --this.dist;

      if(this.x<0 || this.x>w || this.y<0 || this.y>h) dead = true;
      
      if(this.dist<=0 && this.width>1) {
        
        // keep yo self, sometimes
        this.dist = Math.random() * ( maxDist - minDist ) + minDist;
        
        // add 2 children
        if(lines.length < maxLines ) lines.push(new Line(this));
        if(lines.length < maxLines && Math.random() < .5) lines.push(new Line(this));
        
        // kill the poor thing
        if( Math.random() < .2 ) dead = true;
      }
      ctx.strokeStyle = ctx.shadowColor = '#2c2';
      ctx.beginPath();
      ctx.lineWidth = this.width;
      ctx.moveTo(this.x, this.y);
      ctx.lineTo( prevX, prevY );
      ctx.stroke();
      
      if( dead ) return true
    }

    init();
    anim();

    window.addEventListener( 'resize', function() {
      window.location.reload();
      w = window.innerWidth;
      h = window.innerHeight;
      starter.x = w / 2;
      starter.y = h / 2;
      init();
    } )
  </script>
  <!--表单控制代码-->
  <script type="text/javascript">
    window.addEventListener("load", () => {
      //当前登录状态
      if(!localStorage.getItem("login")) {
        localStorage.setItem("login", 0); //0未登录 1游客 2管理员
        document.getElementById("account-name").innerText = "未登录";
        document.getElementById("query-result-button-group").classList.add("hidden");
      } else if(localStorage.getItem("login") == 0) {
        document.getElementById("login").classList.remove("exit");
        document.getElementById("account-name").innerText = "未登录";
        document.getElementById("query-result-button-group").classList.add("hidden");
      } else if(localStorage.getItem("login") == 2){
        document.getElementById("login").classList.add("exit");
        document.getElementById("account-name").innerText = "欢迎，管理员 admin";
        document.getElementById("query-result-button-group").classList.remove("hidden");
      } else if(localStorage.getItem("login") == 1) {
        document.getElementById("login").classList.add("exit");
        document.getElementById("account-name").innerText = "欢迎，访客 guest";
        document.getElementById("query-result-button-group").classList.add("hidden");
      }
    })
    

    let adminAccount = "admin";
    let adminPassword = "admin@beihang123";
    let guestAccount = "guest";
    let guestPassword = "guest@feiyun456";

    function login() {
      let account = document.getElementById("login-account-input").value;
      let password = document.getElementById("login-password-input").value;
      if(account === adminAccount && password === adminPassword) {
        localStorage.setItem("login", 2);
        document.getElementById("login").classList.add("exit");
        document.getElementById("account-name").innerText = "欢迎，管理员 admin";
        document.getElementById("query-result-button-group").classList.remove("hidden");
      } else if (account === guestAccount && password === guestPassword) {
        localStorage.setItem("login", 1);
        document.getElementById("login").classList.add("exit");
        document.getElementById("account-name").innerText = "欢迎，访客 guest";
        document.getElementById("query-result-button-group").classList.add("hidden");
      } else {
        alert("用户名或密码错误！");
      }
    }
  </script>
  <!--登录遮罩样式-->
  <style>
    #login {
      background: #131731;
      z-index: 10;
      width: 100vw;
      height: 100vh;
      position: absolute;
      transform: none;
      transition: transform 1s ease-in-out;
    }
    #login.exit{
      transform: translate(0,-100vh);
      transition: transform 1s ease-in-out;
    }
    #login .content-wrapper{
      position: absolute;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      z-index: 20;
      
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    #login .content-wrapper h1{
      color: white;
      font-size: 80px;
      margin: 40px;
    }
    #login .content-wrapper .card{
      margin: 80px;
      width: 400px;
      height: 300px;
      background-color: #2d2f78a0;
      z-index: 30;
      box-shadow: 0 0 0 60px #00000050;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    #login .content-wrapper .card .item{
      margin: 20px;
    }
    #login .content-wrapper .card .item .label{
      font-size: 24px;
      font-weight: bolder;
      color: white;
      margin-right: 40px;
    }
  </style>
  <!--本体-->
  <div id="root-div">
    <div class="header">
      <div class="logo-wrapper">
        <img class="logo" src="assets/logo.svg" height="50%">
        <h1>WSDL系统图谱交互平台</h1>
      </div>
      <div class="account-wrapper">
        <span id="account-name">未登录</span>
        <div class="button danger hidden" onclick="logout()">退出</div>
        <script type="text/javascript">
          function logout() {
            if (confirm("确认退出登录吗？")) {
              localStorage.setItem("login", 0);
              document.getElementById("login").classList.remove("exit");
              document.getElementById("account-name").innerText = "未登录";
              document.getElementById("query-result-button-group").classList.add("hidden");
            }
          }
        </script>
      </div>
    </div>
    <div class="content">
      <div class="col col-8">
        <div class="headline">
          <h1>节点关系图</h1>
          <div>
            <input id="query-keyword" class="input" type="text" placeholder="输入要查询的节点名称">
            <div class="button" onclick="search()">查询</div>
            <!--查询节点功能-->
            <script type="text/javascript">
              async function search() {
                event.stopPropagation; //我为啥写了这一行？看起来是个完全没卵用的语句啊
                if(graph === {} || response === {}) return;
                let keyword = document.getElementById("query-keyword").value;
                let results = new Map();
                await api.searchNode(keyword).then((res) => {
                  if(res.data.msg === "success") {
                    for(let i = 0, l = res.data.nodes_id.length; i < l; i++) {
                      results.set(res.data.nodes_id[i].id + "", true);
                    }
                  }
                }).catch((err) => {
                  console.log(err);
                })
                let highlighted = 1;
                let lowlighted = keyword ? 0.1 : 1;
                for(let i = 0, len = option.series[0].data.length; i < len; i++) {
                  if (option.series[0].data[i].node_name === keyword || results.get(option.series[0].data[i].name)) {
                    console.log("found one");
                    option.series[0].data[i].itemStyle = {opacity: highlighted,};
                    showNodeInfo(option.series[0].data[i].index);
                  } else {
                    option.series[0].data[i].itemStyle = {opacity: lowlighted,};
                  }
                }
                if(graph) graph.setOption(option);
              }
            </script>
          </div>
        </div>
        <div id="graph"></div>
      </div>
      <div class="col col-4">
        <div id="query-result">
          <div class="title">
            <div class="text">节点#?: ???</div>
            <div class="button-group">
              <span class="button" onclick="downloadWsdl()">
                <span class="mdi mdi-download"></span>
              </span>
              <script type="text/javascript">
                let currentNode = -1;
                function downloadWsdl() {
                  if(currentNode === -1) {
                    return;
                  }
                  //下载函数
                  function download(filename, text) {
                    let element = document.createElement('a');
                    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
                    element.setAttribute('download', filename);
                    element.style.display = 'none';
                    document.body.appendChild(element);
                    element.click();
                    document.body.removeChild(element);
                  }
                  api.downloadWsdl(currentNode).then((res) => {
                    if(!res.data.msg) {
                      download(`NodeDetails-Node${currentNode}.wsdl`, res.data);
                    }
                    else throw "Invalid node";
                  }).catch((err) => {
                    alert(err);
                  })
                }
              </script>
            </div>
          </div>
          <div class="entry group_id"><div class="label">节点分组号</div><div class="value"></div></div>
          <div class="button-group" id="query-result-button-group">
            <div class="button danger" onclick="deleteNode()">删除当前节点</div>
            <div class="button success" onclick="toggleCreateForm(currentNode)">添加子节点</div>
            <script type="text/javascript">
              function deleteNode() {
                api.deleteNode(currentNode).then((res) => {
                  if(res.data.code === 0) {
                    alert("删除成功");
                    window.location.reload();
                  } 
                  else throw "failed to delete";
                }).catch((err) => {
                  alert(err);
                })
              }
            </script>
          </div>
          <hr>
          <h3>资源参数</h3>
          <div class="entry ncpus"><div class="label">CPU核数</div><div class="value"></div></div>
          <div class="entry ngpus"><div class="label">GPU卡数</div><div class="value"></div></div>
          <div class="entry queue"><div class="label">队列</div><div class="value"></div></div>
          <div class="entry nnodes"><div class="label">节点数量</div><div class="value"></div></div>
          <div class="entry node_mem"><div class="label">节点内存</div><div class="value"></div></div>
          <div class="entry node_cpus"><div class="label">节点核心数</div><div class="value"></div></div>
          <div class="entry node_cputype"><div class="label">节点CPU类型</div><div class="value"></div></div>
          <div class="entry node_storage"><div class="label">节点存储容量</div><div class="value"></div></div>
          <div class="entry node_type"><div class="label">节点类型</div><div class="value"></div></div>
          <div class="entry node_os"><div class="label">节点系统类型</div><div class="value"></div></div>
          <hr>
          <h3>运行高性能中心参数</h3>
          <div class="entry sccenter_name"><div class="label">中心名称</div><div class="value"></div></div>
          <div class="entry sccenter_location"><div class="label">地理位置</div><div class="value"></div></div>
          <hr>
          <h3>账号参数</h3>
          <div class="entry sc_user"><div class="label">超算账号</div><div class="value"></div></div>
          <hr>
          <h3>服务基本参数</h3>
          <div class="entry software"><div class="label">应用名称</div><div class="value"></div></div>
          <div class="entry software_version"><div class="label">应用版本</div><div class="value"></div></div>
          <div class="entry software_type"><div class="label">应用分类</div><div class="value"></div></div>
          <div class="entry software_outline"><div class="label">应用概要</div><div class="value"></div></div>
          <hr>
          <h3>服务协议</h3>
          <div class="entry service_type"><div class="label">服务协议类型</div><div class="value"></div></div>
          <hr>
          <h3>输入文件</h3>
          <div class="entry inputfiles_m"><div class="label">必须输入文件</div><div class="value"></div></div>
          <div class="entry inputdirs_o"><div class="label">可选文件夹</div><div class="value"></div></div>
          <div class="entry inputfiles_o"><div class="label">可选文件</div><div class="value"></div></div>
          <hr>
          <h3>输出说明</h3>
          <div class="entry outpath"><div class="label">输出路径</div><div class="value"></div></div>
          <hr>
          <h3>其他参数</h3>
          <div class="entry limit_time"><div class="label">时间上限(分)</div><div class="value"></div></div>
          <!--显示查询或点击结果-->
          <script type="text/javascript">
            function showNodeInfo(index) {
              if(response === {}) return;
              let node = response.nodes[index];
              currentNode = node.id;
              document.querySelector('#query-result .title .text').innerHTML = `节点#${node.id}: ${node.node_name}`
              Object.getOwnPropertyNames(node).forEach((key) => {
                let dom = document.querySelector(`#query-result .${key} .value`);
                if(dom === null) return;
                if(key === "group_id") dom.innerHTML = `${node[key]} - ${categories[node[key]].name}`;
                else dom.innerHTML = node[key] ? node[key] : '--';
              })
            }
          </script>
        </div>
        <div id="create-form" class="exit">
          <div class="title">
            <div class="text">添加节点到节点 #???</div>
            <div class="button-group">
              <span class="button" onclick="toggleCreateForm(null)">
                <span class="mdi mdi-arrow-left"></span>
              </span>
              <script type="text/javascript">
                let addedNode = {};
                function toggleCreateForm(node) {
                  let createFormDom = document.getElementById("create-form");
                  if(createFormDom.classList.contains("exit")) { //已经隐藏
                    document.querySelector("#create-form .title .text").innerHTML = `添加节点到节点 #${node}`;
                    addedNode.src = node;
                    addedNode.data = {};
                    createFormDom.classList.remove("exit");
                  } else { //已展开
                    createFormDom.classList.add("exit");
                    addedNode = {};
                  }
                }
              </script>
            </div>
          </div>
          <hr>
          <h3>基本参数</h3>
          <div class="entry node_name"><div class="label">节点名称</div><input id="input-node_name"></div>
          <hr>
          <h3>资源参数</h3>
          <div class="entry ncpus"><div class="label">CPU核数</div><input id="input-ncpus"></div>
          <div class="entry ngpus"><div class="label">GPU卡数</div><input id="input-ngpus"></div>
          <div class="entry queue"><div class="label">队列</div><input id="input-queue"></div>
          <div class="entry nnodes"><div class="label">节点数量</div><input id="input-nnodes"></div>
          <div class="entry node_mem"><div class="label">节点内存</div><input id="input-node_mem"></div>
          <div class="entry node_cpus"><div class="label">节点核心数</div><input id="input-node_cpus"></div>
          <div class="entry node_cputype"><div class="label">节点CPU类型</div><input id="input-node_cputype"></div>
          <div class="entry node_storage"><div class="label">节点存储容量</div><input id="input-node_storage"></div>
          <div class="entry node_type"><div class="label">节点类型</div><input id="input-node_type"></div>
          <div class="entry node_os"><div class="label">节点系统类型</div><input id="input-node_os"></div>
          <hr>
          <h3>运行高性能中心参数</h3>
          <div class="entry sccenter_name"><div class="label">中心名称</div><input id="input-sccenter_name"></div>
          <div class="entry sccenter_location"><div class="label">地理位置</div><input id="input-sccenter_location"></div>
          <hr>
          <h3>账号参数</h3>
          <div class="entry sc_user"><div class="label">超算账号</div><input id="input-sc_user"></div>
          <hr>
          <h3>服务基本参数</h3>
          <div class="entry software"><div class="label">应用名称</div><input id="input-software"></div>
          <div class="entry software_version"><div class="label">应用版本</div><input id="input-software_version"></div>
          <div class="entry software_type"><div class="label">应用分类</div><input id="input-software_type"></div>
          <div class="entry software_outline"><div class="label">应用概要</div><input id="input-software_outline"></div>
          <hr>
          <h3>服务协议</h3>
          <div class="entry service_type"><div class="label">服务协议类型</div><input id="input-service_type"></div>
          <hr>
          <h3>输入文件</h3>
          <div class="entry inputfiles_m"><div class="label">必须输入文件</div><input id="input-inputfiles_m"></div>
          <div class="entry inputdirs_o"><div class="label">可选文件夹</div><input id="input-inputdirs_o"></div>
          <div class="entry inputfiles_o"><div class="label">可选文件</div><input id="input-inputfiles_o"></div>
          <hr>
          <h3>输出说明</h3>
          <div class="entry outpath"><div class="label">输出路径</div><input id="input-outpath"></div>
          <hr>
          <h3>其他参数</h3>
          <div class="entry limit_time"><div class="label">时间上限(分)</div><input id="input-limit_time"></div>
          <div class="entry other"><div class="label">备注信息</div><input id="input-other"></div>
          <hr>
          <div class="entry button-group"><span class="button success" onclick="submitAddNode()">提交</span></div>
          <script type="text/javascript">
            function submitAddNode() {
              addedNode.data = {
                inputdirs_o: "",
                inputfiles_m: "",
                inputfiles_o: "",
                limit_time: "",
                ncpus: "",
                ngpus: "",
                nnodes: "",
                node_cpus: "",
                node_cputype: "",
                node_mem: "",
                node_name: "",
                node_os: "",
                node_storage: "",
                node_type: "",
                other: "",
                outpath: "",
                queue: "",
                sc_user: "",
                sccenter_location: "",
                sccenter_name: "",
                service_type: "",
                software: "",
                software_outline: "",
                software_type: "",
                software_version: ""
              }

              Object.getOwnPropertyNames(addedNode.data).forEach((name) => {
                addedNode.data[name] = document.getElementById(`input-${name}`).value;
              })

              addedNode.data.child_num = 0; //default, useless
              addedNode.data.group_id = 0;  //default, useless
              addedNode.data.id = 0;        //default, useless
              
              api.createNode(addedNode).then((res) => {
                if(res.data.msg === "success") {
                  alert("添加成功");
                  window.location.reload();
                } else {
                  throw "failed to create"
                }
              }).catch((err) => {
                console.log(err);
              })
            }
          </script>
        </div>
      </div>
    </div>
    <div class="footer"></div>
  </div>
</body>
<!--script for echarts-->
<script>
//引入一个svg的path，这是图谱root节点的图标
const rootSymbolPath = "path://M903.3142700195312,842.1714477539062c0.22857666015625,-0.3428955078125,0.57147216796875,-0.68572998046875,0.79998779296875,-1.028564453125C978.971435546875,752.1142578125,1024,637.3713989257812,1024,512s-45.028564453125,-240.11428833007812,-119.77142333984375,-329.1428527832031c-0.22857666015625,-0.342864990234375,-0.5714111328125,-0.5714263916015625,-0.79998779296875,-0.9142913818359375c-1.25714111328125,-1.4857177734375,-2.4000244140625,-2.857147216796875,-3.65716552734375,-4.22857666015625c-0.4571533203125,-0.5714263916015625,-0.914306640625,-1.028564453125,-1.37139892578125,-1.5999908447265625l-4.68572998046875,-5.371429443359375l-0.11431884765625,-0.114288330078125c-1.7142333984375,-1.9428558349609375,-3.5428466796875,-3.885711669921875,-5.25714111328125,-5.8285675048828125l-0.1142578125,-0.114288330078125c-3.65716552734375,-3.885711669921875,-7.31427001953125,-7.77142333984375,-11.085693359375,-11.542861938476562l-0.11431884765625,-0.114288330078125l-5.4857177734375,-5.4857025146484375l-0.34283447265625,-0.342864990234375c-1.71429443359375,-1.7142791748046875,-3.4285888671875,-3.3142852783203125,-5.14288330078125,-4.914276123046875c-0.5714111328125,-0.571441650390625,-1.142822265625,-1.1428680419921875,-1.82855224609375,-1.71429443359375c-1.14288330078125,-1.142852783203125,-2.28570556640625,-2.1714324951171875,-3.4285888671875,-3.1999969482421875c-0.34283447265625,-0.342864990234375,-0.79998779296875,-0.6857147216796875,-1.142822265625,-1.142852783203125C768.4571533203125,51.657142639160156,646.2857055664062,0,512,0s-256.45713806152344,51.657142639160156,-347.77142333984375,136.22857666015625c-0.342864990234375,0.3428497314453125,-0.8000030517578125,0.6857147216796875,-1.1428680419921875,1.142852783203125c-1.142852783203125,1.028564453125,-2.28570556640625,2.1714324951171875,-3.428558349609375,3.3142852783203125c-0.571441650390625,0.5714263916015625,-1.1428680419921875,1.142852783203125,-1.828582763671875,1.7142791748046875c-1.7142791748046875,1.600006103515625,-3.4285736083984375,3.3142852783203125,-5.142852783203125,4.9142913818359375l-0.342864990234375,0.342864990234375l-5.4857025146484375,5.4857025146484375l-0.114288330078125,0.114288330078125c-3.77142333984375,3.77142333984375,-7.4285736083984375,7.6571502685546875,-11.085708618164062,11.542861938476562l-0.114288330078125,0.114288330078125c-1.828582763671875,1.9428558349609375,-3.5428619384765625,3.885711669921875,-5.25714111328125,5.8285675048828125l-0.114288330078125,0.114288330078125c-1.600006103515625,1.7142791748046875,-3.2000045776367188,3.5428466796875,-4.6857147216796875,5.371429443359375c-0.45714569091796875,0.5714263916015625,-0.9142913818359375,1.028564453125,-1.371429443359375,1.5999908447265625c-1.2571487426757812,1.371429443359375,-2.4000015258789062,2.857147216796875,-3.6571426391601562,4.22857666015625c-0.22857666015625,0.3428497314453125,-0.5714340209960938,0.5714263916015625,-0.8000030517578125,0.9142913818359375C45.02857208251953,271.8857116699219,0,386.6285705566406,0,512s45.02857208251953,240.1142578125,119.77143096923828,329.14288330078125c0.22856903076171875,0.34283447265625,0.5714263916015625,0.6856689453125,0.7999954223632812,1.028564453125l3.5428619384765625,4.22857666015625c0.4571380615234375,0.5714111328125,0.9142837524414062,1.028564453125,1.371429443359375,1.5999755859375l4.6857147216796875,5.37139892578125c0,0.11431884765625,0.114288330078125,0.11431884765625,0.114288330078125,0.22857666015625c1.7142791748046875,1.94287109375,3.428558349609375,3.8857421875,5.25714111328125,5.71429443359375l0.114288330078125,0.11431884765625c3.657135009765625,3.88568115234375,7.3142852783203125,7.77142333984375,10.971420288085938,11.5428466796875l0.114288330078125,0.1142578125c1.8285675048828125,1.82861328125,3.5428619384765625,3.65716552734375,5.371429443359375,5.3714599609375l0.3428497314453125,0.34283447265625c3.7714385986328125,3.77142333984375,7.6571502685546875,7.4285888671875,11.542861938476562,10.971435546875c91.54286193847656,84.5714111328125,213.71429443359375,136.22857666015625,348,136.22857666015625s256.4571533203125,-51.65716552734375,347.77142333984375,-136.22857666015625a342.857143,342.857143,0,0,0,11.4285888671875,-10.971435546875l0.34283447265625,-0.34283447265625c1.82855224609375,-1.82855224609375,3.65716552734375,-3.5428466796875,5.3714599609375,-5.3714599609375l0.1142578125,-0.1142578125c3.77142333984375,-3.77142333984375,7.4285888671875,-7.65716552734375,10.971435546875,-11.5428466796875l0.1142578125,-0.11431884765625c1.71429443359375,-1.94287109375,3.54290771484375,-3.77142333984375,5.25714111328125,-5.71429443359375c0,-0.1142578125,0.11431884765625,-0.1142578125,0.11431884765625,-0.22857666015625c1.5999755859375,-1.7142333984375,3.20001220703125,-3.5428466796875,4.68572998046875,-5.37139892578125c0.4571533203125,-0.5714111328125,0.91424560546875,-1.028564453125,1.37139892578125,-1.5999755859375a113.142857,113.142857,0,0,0,3.77142333984375,-4.22857666015625Zm4.68572998046875,-162.971435546875c-15.77142333984375,37.25714111328125,-36.5714111328125,71.77142333984375,-61.94287109375,103.085693359375a507.508571,507.508571,0,0,0,-93.142822265625,-63.88568115234375c13.25714111328125,-53.60003662109375,21.4857177734375,-112.4571533203125,23.6571044921875,-174.4000244140625L940.5714111328125,544c-3.42852783203125,46.74285888671875,-14.39996337890625,92.1142578125,-32.5714111328125,135.20001220703125ZM940.5714111328125,480L776.5714111328125,480c-2.17138671875,-61.94287109375,-10.39996337890625,-120.79998779296875,-23.6571044921875,-174.39999389648438c33.4857177734375,-17.828582763671875,64.6856689453125,-39.314300537109375,93.142822265625,-63.88572692871094A427.268571,427.268571,0,0,1,940.5714111328125,480ZM679.2000122070312,116c45.37139892578125,19.199996948242188,86.6285400390625,45.71427917480469,122.971435546875,79.08570861816406a451.108571,451.108571,0,0,1,-67.8857421875,47.77143859863281c-17.94287109375,-51.42857360839844,-40.914306640625,-96.11428833007812,-67.6571044921875,-131.8857192993164c4.228515625,1.5999984741210938,8.45709228515625,3.3142852783203125,12.5714111328125,5.028572082519531Zm-103.5428466796875,800.6857299804688c-10.5142822265625,8.22857666015625,-21.028564453125,14.5142822265625,-31.65716552734375,18.74285888671875L544,723.4285888671875a444.685714,444.685714,0,0,1,132.22857666015625,29.94281005859375c-9.4857177734375,28.11431884765625,-20.4571533203125,54.05718994140625,-33.14288330078125,77.4857177734375c-19.88568115234375,37.02862548828125,-43.199951171875,66.62860107421875,-67.42852783203125,85.82861328125Zm67.42852783203125,-723.5428771972656c12.57147216796875,23.542861938476562,23.65716552734375,49.4857177734375,33.14288330078125,77.4857177734375A444.685714,444.685714,0,0,1,544,300.5714416503906L544,88.68571472167969c10.5142822265625,4.228569030761719,21.14288330078125,10.400001525878906,31.65716552734375,18.74285888671875c24.22857666015625,19.085708618164062,47.5428466796875,48.68571472167969,67.42852783203125,85.71427917480469ZM544,659.3142700195312L544,544l168.5714111328125,0c-1.82855224609375,50.5142822265625,-8.1142578125,99.5428466796875,-18.6285400390625,146.05712890625l-0.3428955078125,1.3714599609375A508.594286,508.594286,0,0,0,544,659.3142700195312Zm0,-179.31427001953125L544,364.6856994628906c52.34283447265625,-3.199981689453125,102.62860107421875,-14.28570556640625,149.5999755859375,-32.1142578125l0.3428955078125,1.371429443359375c10.5142822265625,46.5142822265625,16.79998779296875,95.42855834960938,18.6285400390625,146.05712890625L544,480Zm-64,64l0,115.31427001953125c-52.342864990234375,3.20001220703125,-102.62857055664062,14.28570556640625,-149.60000610351562,32.11431884765625l-0.342864990234375,-1.3714599609375c-10.5142822265625,-46.5142822265625,-16.79998779296875,-95.42852783203125,-18.628570556640625,-146.05712890625L480,544Zm-168.57144165039062,-64c1.828582763671875,-50.5142822265625,8.114288330078125,-99.5428466796875,18.628570556640625,-146.05712890625l0.342864990234375,-1.371429443359375c46.971435546875,17.82855224609375,97.14285278320312,28.914276123046875,149.60000610351562,32.1142578125L480,480L311.4285583496094,480ZM480,723.4285888671875l0,211.88568115234375c-10.5142822265625,-4.22857666015625,-21.142852783203125,-10.39996337890625,-31.657135009765625,-18.74285888671875c-24.22857666015625,-19.085693359375,-47.65716552734375,-48.79998779296875,-67.54287719726562,-85.82855224609375c-12.5714111328125,-23.5428466796875,-23.657135009765625,-49.4857177734375,-33.142852783203125,-77.4857177734375c42.5142822265625,-16.68572998046875,86.74285888671875,-26.6285400390625,132.34286499023438,-29.82855224609375Zm0,-422.8571472167969a444.685714,444.685714,0,0,1,-132.22857666015625,-29.94287109375c9.4857177734375,-28.114288330078125,20.4571533203125,-54.05714416503906,33.142852783203125,-77.4857177734375c19.885711669921875,-37.028564453125,43.20001220703125,-66.74285125732422,67.54287719726562,-85.82856750488281c10.5142822265625,-8.228569030761719,21.028564453125,-14.5142822265625,31.657135009765625,-18.74285888671875L480.1142883300781,300.5714416503906ZM344.79998779296875,116c4.22857666015625,-1.7142868041992188,8.342864990234375,-3.4285736083984375,12.571441650390625,-5.028572082519531c-26.74285888671875,35.77143096923828,-49.71429443359375,80.45714569091797,-67.65713500976562,131.8857192993164c-24,-13.71429443359375,-46.74285888671875,-29.71429443359375,-67.88572692871094,-47.77143859863281c36.34284973144531,-33.371429443359375,77.59999084472656,-59.885711669921875,122.97142028808594,-79.08570861816406ZM116,344.79998779296875c15.77142333984375,-37.25714111328125,36.57142639160156,-71.77142333984375,61.94285583496094,-103.08570861816406c28.457138061523438,24.571426391601562,59.65715026855469,46.05714416503906,93.14286804199219,63.88572692871094c-13.25714111328125,53.600006103515625,-21.4857177734375,112.45712280273438,-23.657150268554688,174.39999389648438L83.42857360839844,480c3.4285659790039062,-46.74285888671875,14.399993896484375,-92.11428833007812,32.57142639160156,-135.20001220703125ZM83.42857360839844,544l164,0c2.1714324951171875,61.94287109375,10.400009155273438,120.79998779296875,23.657150268554688,174.4000244140625a507.508571,507.508571,0,0,0,-93.14286804199219,63.88568115234375A427.268571,427.268571,0,0,1,83.42857360839844,544Zm261.3714141845703,364c-45.371429443359375,-19.20001220703125,-86.62857055664062,-45.71429443359375,-122.97142028808594,-79.085693359375c21.142868041992188,-18.05718994140625,43.88572692871094,-33.94287109375,67.88572692871094,-47.77142333984375c17.942840576171875,51.42852783203125,40.914276123046875,96.1142578125,67.65713500976562,131.88568115234375c-4.22857666015625,-1.5999755859375,-8.4571533203125,-3.31427001953125,-12.571441650390625,-5.028564453125Zm334.4000244140625,0c-4.22857666015625,1.71429443359375,-8.3428955078125,3.4285888671875,-12.5714111328125,5.028564453125c26.7427978515625,-35.77142333984375,49.7142333984375,-80.4571533203125,67.6571044921875,-131.88568115234375c24,13.7142333984375,46.74285888671875,29.7142333984375,67.8857421875,47.77142333984375a427.211429,427.211429,0,0,1,-122.971435546875,79.085693359375Z";
//echarts默认调色板
const colorSeries = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']
let response = {};//getAll接口返回的数据缓存
const categories = [
  {
    name: "ROOT", 
    symbol: rootSymbolPath, //categories
    symbolSize: [40,40],
    icon: rootSymbolPath, //legend
    itemStyle: {
      color: '#0000CD',
      shadowColor: '#191970',
      shadowBlur: 20,
    },
  },
  {name: "海洋环境（WRF）", itemStyle: {color:colorSeries[1], shadowColor: colorSeries[1], }},
  {name: "工业制造（ABAQUS/LS-DYNA/StarCCM+）", itemStyle: {color:colorSeries[2], shadowColor: colorSeries[2], }},
  {name: "材料科学（CALYPSO）", itemStyle: {color:colorSeries[3], shadowColor: colorSeries[3], }},
  {name: "分子动力学（LAMMPS/gromacs/namd）", itemStyle: {color:colorSeries[4], shadowColor: colorSeries[4], }},
  {name: "流体力学（fluent）", itemStyle: {color:colorSeries[5], shadowColor: colorSeries[5], }},
];
let option = { //echarts option对象
  legend: {
    data: categories,
  },
  series: [
    {
      type: 'graph',
      layout: 'force',
      zoom: 3,
      force: {
        //layoutAnimation: false,
        repulsion: 40,
        //gravity: 0.1,
        edgeLength: 10,
      },
      roam: true,
      data: [],
      itemStyle: {
      },
      links: [],
      lineStyle: {
        color: 'source',
        width: 3,
        curveness: 0.2,
      },
      categories: categories,
      label: {
        show: true,
        formatter:(obj) => `${obj.data.node_name}`,
        fontSize: 16,
        fontWeight: 'bolder',
      },
    },    
  ],
};

let graph = {};
window.onload = () => {
  let graphDom = document.getElementById("graph");
  graph = echarts.init(graphDom);

  let promiseGetAll = api.getAll().then((res) => {
    if(res.data.msg === "success") {
      response = res.data;
    }
  }).catch((err) => {
    console.log(err);
  });

  //等待getAll返回后执行
  Promise.all([promiseGetAll]).then(() => {
    option.series[0].data = [];
    option.series[0].links = [];
    for(let i = 0, len = response.nodes.length; i < len; i++) {
      let node = response.nodes[i];
      option.series[0].data.push({
        name: node.id + "",
        category: node.group_id,
        node_name: node.node_name,
        index: i,
        symbolSize: node.child_num > 0 ? [30,30] : [20,20],
        itemStyle: (node.child_num > 0) && (node.id !== 0) ? {
          shadowBlur: 15,
        } : {},
      })
    }
    for(let i = 0, len = response.edges.length; i < len; i++) {
      let edge = response.edges[i];
      option.series[0].links.push({
        source: edge.src + "",
        target: edge.dst + "",
      })
    }
  
    graph.setOption(option);
    showNodeInfo(0);
  }).catch((err) => {
    console.log(err);
  })

  //挂载点击事件
  graph.on('click', (params) => {
    showNodeInfo(params.data.index);
  })
}
</script>
<style>
/*
配色：
背景#F5F5F5 卡片背景#E0E0E2 标题与按钮#2a2d79 红色#cd5c45 绿色#74a916
*/
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;}
/*滚动条美化*/
*::-webkit-scrollbar {
  width: 5px;
  height: 5px;}
*::-webkit-scrollbar-thumb {
  /*滚动条里面小方块*/
  border-radius: 15px;
  -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  /*background: #cce2f7;*/
  /* background: #999; */}
*::-webkit-scrollbar-track {
  /*滚动条里面轨道*/
  -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  border-radius: 10px;
  background: #ededed;}
*:-webkit-autofill {
  box-shadow: 0 0 0px 1000px #323451 inset !important;
  -webkit-box-shadow: 0 0 0px 1000px #323451 inset !important;
  border: 0px solid #CCC!important;
  -webkit-text-fill-color: #FFF;}

#root-div{
  width: 100vw;
  height: 100vh;
  background: #f5f5f5;
  overflow: hidden;

  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;}
.header{
  width: 100%;
  height: 96px;
  background-color: #2a2d79;
  box-shadow: 0 10px 10px gray;

  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;}
.header .logo-wrapper{
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  width: 50%;
  height: 96px;}
.header .logo{
  margin: 0 32px 0 32px;}
.header h1{
  color: white;}
.header .account-wrapper{
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  align-items: center;
  width: 50%;
  height: 96px;}
.header .account-wrapper span{
  font-size: 24px;
  font-weight: bolder;
  color: white;}
.content{
  height: calc(0.8 * (100vh - 96px));
  width: 80%;
  border: 1px solid gray;
  border-radius: 16px;
  box-shadow: 10px 10px 10px gray;
  overflow: hidden;

  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;}
.content .col{
  height: 100%;
  padding: 16px;}
.content .col-8{
  width: 66.6%;}
.content .col-8 .headline{
  width: 100%;
  height: 64px;

  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;}
.content .col-8 .headline h1{
  color: #2a2d79;}
.content .col-4{
  width: 33.3%;
  background: #e0e0e2;
  border-left: 1px solid gray;
  overflow: hidden;
  word-wrap: break-word;
  word-break: break-all;
  position: relative;}

.button{
  display: inline-block;
  vertical-align: top;
  border: 1px solid #2a2d79;
  margin: 0 8px 0 8px;
  padding: 4px 8px 4px 8px;
  cursor: pointer;
  border-radius: 4px;
  height: 32px;

  background: #2a2d79;
  color: white;
  font-weight: bolder;}
.button:hover{
  box-shadow: 0 0 10px #494cc4;}
.button:active {
  box-shadow: 0 0 10px #494cc4;
  background: #494cc4;}

.button.success{
  display: inline-block;
  vertical-align: top;
  border: 1px solid #74a916;
  margin: 0 8px 0 8px;
  padding: 4px 8px 4px 8px;
  cursor: pointer;
  border-radius: 4px;
  height: 32px;

  background: #74a916;
  color: white;
  font-weight: bolder;}
.button.success:hover{
  box-shadow: 0 0 10px #a8f51f;}
.button.success:active {
  box-shadow: 0 0 10px #a8f51f;
  background: #a8f51f;}

.button.danger{
  display: inline-block;
  vertical-align: top;
  border: 1px solid #cd5c45;
  margin: 0 8px 0 8px;
  padding: 4px 8px 4px 8px;
  cursor: pointer;
  border-radius: 4px;
  height: 32px;

  background: #cd5c45;
  color: white;
  font-weight: bolder;}
.button.danger:hover{
  box-shadow: 0 0 10px #ff7255;}
.button.danger:active {
  box-shadow: 0 0 10px #ff7255;
  background: #ff7255;}

input{
  outline-style: none;
  line-height: 32px;
  height: 32px;
  border: 1px solid #2a2d79;
  border-radius: 4px;
  padding: 4px 8px 4px 8px;}
input:focus{
  box-shadow: 0 0 10px #494cc4;}

#graph{
  height: calc(100% - 64px - 32px);
  margin-top: 32px;
  width: 100%;}
#query-result{
  width: 100%;
  height: 100%;
  overflow-y: auto;}
#query-result .entry{
  margin: 4px 0 4px 0;}
#query-result .entry .label{
  display: inline-block;
  width: 7em;
  vertical-align: text-top;
  text-align: end;
  
  font-weight: bolder;
  color: black;}
#query-result .entry .value{
  display: inline-block;
  width: calc(100% - 8em);
  margin-left: 1em;
  vertical-align: text-top;
  
  font-size: 1.1em;}
#query-result hr{
  margin: 12px 0 12px 0;}
#query-result .title{
  margin-bottom: 12px;
  color: #2a2d79;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;}
#query-result .title .text{
  font-size: 1.17em;
  font-weight: bolder;
  color: #2a2d79;}
#query-result h3{
  color: #2a2d79;
  font-weight: bolder;}
#query-result .button-group{
  display: flex;
  margin: 12px;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;}
#query-result .button-group.hidden{
  display: none;}

#create-form{
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background-color: #ffffff;
  overflow-y: auto;
  padding: 16px;
  transform: none;
  transition: transform 0.5s ease-in-out;}
#create-form.exit{
  transform: translate(-110%, 0);
  transition: transform 0.5s ease-in-out;}
#create-form .title{
  margin-bottom: 12px;
  color: #2a2d79;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;}
#create-form .title .text{
  font-size: 1.17em;
  font-weight: bolder;
  color: #2a2d79;}
#create-form h3{
  margin-bottom: 12px;
  color: #2a2d79;
  font-weight: bolder;}
#create-form .entry{
  margin: 8px 0 8px 0;}
#create-form .entry .label{
  display: inline-block;
  width: 7em;
  vertical-align: text-top;
  text-align: end;
  
  font-weight: bolder;
  color: black;}
#create-form .entry input{
  display: inline-block;
  width: calc(100% - 8em);
  margin-left: 1em;
  vertical-align: text-top;
  
  font-size: 1.1em;}
#create-form .entry textarea{
  display: inline-block;
  width: calc(100% - 8em);
  margin-left: 1em;
  vertical-align: text-top;

  font-size: 1.1em;}
#create-form hr{
  margin: 12px 0 12px 0;}
#create-form .button-group{
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;}
</style>
</html>